FUNCTION DBSEEK(*WI:DBWRKINDEX; WHICHITEM:INTEGER):DBERRTYPE*);
(*move pointer to item # itemnum in current level*)
VAR NEWOFFSET,I,LINKV:INTEGER;
  CRACKSW:CRACKSWTYPE;
  RP:RECDESPTR;

  PROCEDURE FOLLOWLINKS(NEWOFFSET:PAGEPTR; COUNT:INTEGER);
  VAR I:INTEGER;
  BEGIN
    (*all items assumed to be linked & all lists stopped with nul*)
    WITH WRKTABLE[WI] DO
      WITH WIB^[TOS] DO
        BEGIN
          LINKV:=LINKVALUE(WA,NEWOFFSET);
          (*following should be in external procedure for speed*)
          I:=0;
          WHILE (LINKV > 0 ) AND (I < COUNT) DO
            BEGIN
              NEWOFFSET:=NEWOFFSET+LINKV;
              LINKV:=LINKVALUE(WA,NEWOFFSET);
              I:=I+1;
            END;
          (*end of external proc*)
          IF (LINKV = 0) AND (I < COUNT) THEN
            DBSEEK:=27  (*cannot find requested item*)
          ELSE
            BEGIN
              OFFSET:=NEWOFFSET;
              ITEMNUM:=ITEMNUM+COUNT;
            END;
        END (*WITH WIB*);
  END (*FOLLOWLINKS*);
  
BEGIN (*DBSEEK*)
  DBSEEK:=0;
  TRACEWA(9,WI);
  WITH WRKTABLE[WI] DO
    WITH WIB^[TOS] DO
      BEGIN
        DBSHOWERR('SEEK#1',DBHEAD(WI));
        IF DBTYPECHECK THEN
          BEGIN (*assume that we are at head of this level!*)
            CASE LEVEL OF
              GROUPT,RECORDT:
                BEGIN  (*all groups and records are linked*)
                  IF WHICHITEM > 0 THEN
                    BEGIN
                      (*item #0 in a record may contain several fixed fields*)
                      FOLLOWLINKS(OFFSET,WHICHITEM); 
                      SETDESCRIPTORNUM(WI);
                    END;
                END (*GROUPT*);
              FIELDT:
                BEGIN
                  IF WHICHITEM > 0 THEN
                    BEGIN
                      (*now get offset of field within the record*)
                      RP:=ACTIVERECORDS[WIB^[TOS-1].DESCRIPTORNUM];
                      IF RP = NIL THEN
                        DBSEEK:=32
                      ELSE
                        WITH RP^ DO
                          BEGIN
                            IF WHICHITEM < FIRSTLITEMNUM THEN
                              ITEMNUM:=WHICHITEM
                            ELSE
                              BEGIN (*linked field*)
                                ITEMNUM:=FIRSTLITEMNUM-1;
                                FOLLOWLINKS(OFFSET,(WHICHITEM - FIRSTLITEMNUM 
                                                  + ORD(FIRSTLITEMNUM > 0)));
                              END (*linked field*);
                          END (*WITH RP^*);
                        SETDESCRIPTORNUM(WI); 
                    END;
                END (*FIELDT*)
            END (*CASE*);
          END (*IF DBTYPECHECK*)
        ELSE
          FOLLOWLINKS(OFFSET,WHICHITEM);
      END (*WITH*);
  TRACEWA(10,WI);
END (*DBSEEK*);

FUNCTION DBDESCEND(*WI:DBWRKINDEX):DBERRTYPE*);
VAR LINKV:PAGEPTR;
  OLDLVL:DBLEVELTYPE;
  LINKED:BOOLEAN;
  GP:GRPDESPTR;
  RP:RECDESPTR;
  FP:FLDDESPTR;
  CRACKSW:CRACKSWTYPE;

  PROCEDURE DOWNLINK;
  (*move down to head of enclosed level*)
  VAR PARENTOFFSET:PAGEPTR;
  BEGIN
    WITH WRKTABLE[WI] DO
      BEGIN
        WITH WIB^[TOS] DO
          BEGIN
            LINKV:=LINKVALUE(WA,OFFSET);
            PARENTOFFSET:=OFFSET;
          END;
        IF LINKV = 0 THEN
          DBDESCEND:=19 (*at end of list, can't descend*)
        ELSE
          BEGIN
            OLDLVL:=WIB^[TOS].LEVEL;
            IF OLDLVL=NONET THEN
              DBDESCEND:=20 (*can't continue from nonet*)
            ELSE
              BEGIN
                TOS:=TOS+1;
                WITH WIB^[TOS] DO
                  BEGIN
                    OFFSET:=PARENTOFFSET+LINKSIZE(LINKV);
                    IF OLDLVL = GROUPT THEN
                      (*step over group's tag*)
                      STEPLINK(WI);
                    LEVEL:=NEXTLEVEL(OLDLVL);
                    ITEMNUM:=0;
                  END (*WITH*);
              END (*LEVEL<>NONET*);
          END (*LINKV<>0*);
      END (*WITH WRKTABLE*);
  END (*DOWNLINK*);
  
BEGIN (*DBDESCEND*)
  DBDESCEND:=0;
  TRACEWA(11,WI);
  IF DBTYPECHECK THEN
    WITH WRKTABLE[WI] DO
      BEGIN
        CASE WIB^[TOS].LEVEL OF
          GROUPT:
            BEGIN
              (*point to first record in group*)
              GP:=ACTIVEGROUPS[WIB^[TOS].DESCRIPTORNUM];
              IF GP=NIL THEN
                DBDESCEND:=33
              ELSE
                BEGIN
                  DOWNLINK;
                  SETDESCRIPTORNUM(WI);
                END;
            END (*GROUPT*);
          RECORDT:
            BEGIN (*point to first field in record*)
              RP:=ACTIVERECORDS[WIB^[TOS].DESCRIPTORNUM];
              IF RP=NIL THEN
                DBDESCEND:=32
              ELSE
                BEGIN
                  DOWNLINK;
                  SETDESCRIPTORNUM(WI);
                END (*RP<>NIL*);
            END (*RECORDT*);
          FIELDT:
            BEGIN
              (*if the field is structured, point to the contained group*)
              FP:=ACTIVEFIELDS[WIB^[TOS].DESCRIPTORNUM];
              IF FP=NIL THEN
                DBDESCEND:=31
              ELSE
                WITH FP^ DO
                  IF FLDTYPE <> GROUPF THEN
                    DBDESCEND:=34 (*can't descend into a simple field*)
                  ELSE
                    BEGIN
                      DOWNLINK;
                      SETDESCRIPTORNUM(WI);
                    END;
            END (*FIELDT*)
        END (*CASES*);
      END (*WITH WRKTABLE*)
  ELSE
    (*assume that next level, if any, is linked*)
    DOWNLINK;
  TRACEWA(12,WI);
END (*DBDESCEND*);

FUNCTION DBASCEND(*WI:DBWRKINDEX):DBERRTYPE*);
(*return to enclosing level*)
BEGIN
  WITH WRKTABLE[WI] DO
    BEGIN
      IF TOS > 0 THEN
        BEGIN
          WITH WIB^[TOS] DO
            BEGIN
              OFFSET:=0;
              LEVEL:=NONET;
              DESCRIPTORNUM:=-1;
              ITEMNUM:=-1;
            END;
          TOS:=TOS-1;
        END (*TOS> 0*);
    END (*WITH WRKTABLE*);
  TRACEWA(31,WI);
END (*DBASCEND*);

FUNCTION DBFINDREC(*WI:DBWRKINDEX; RULE:DBFINDRULE; FIELDNUM:INTEGER;
                   KEY:STRING; VAR RECNUM:INTEGER;
                   VAR FOUND:BOOLEAN):DBERRTYPE*);
(*locate a record whose FIELDNUM field matches the KEY according to
  the comparison RULE (ascending,descending, or random equals) *)
(*$G+*)
LABEL 1;
VAR FLINKNUM,FN,RN,RLINKV,FOFFSET,DUMMY:INTEGER;
  RP:RECDESPTR;
  DONE:BOOLEAN;
  S:STRING;
BEGIN
  TRACEWA(27,WI);
(*on entry we should be at RECORDT level, with ITEMNUM=0. First find
  out if field is variable and set FLINKNUM*)
  WITH WRKTABLE[WI] DO
    WITH WIB^[TOS] DO
      BEGIN
        IF LEVEL <> RECORDT THEN
          DBFINDREC:=39 (*must be at record level*)
        ELSE
          BEGIN
            IF ITEMNUM <> 0 THEN
              DUMMY:=DBHEAD(WI);
            RP:=ACTIVERECORDS[DESCRIPTORNUM];
            IF RP = NIL THEN
              DBFINDREC:=32
            ELSE
              WITH RP^ DO
                BEGIN
                  IF (SWITCHES <> 0) OR (FIELDNUM < FIRSTLITEMNUM) THEN
                    DBFINDREC:=40 (*must be untagged record and
                                    untagged string field*)
                  ELSE
                    BEGIN
                      FLINKNUM:=FIELDNUM - FIRSTLITEMNUM
                                         + ORD(FIRSTLITEMNUM > 0);
                      DONE:=FALSE;
                      FOUND:=FALSE;
                      RN:=0;
                      RLINKV:=LINKVALUE(WA,OFFSET);
                      (*speed-up possibilities: native code; assume all
                        links are single bytes & eliminate proc calls
                        to linksize & linkvalue*)
                      WHILE RLINKV <> 0 DO
                        BEGIN
                          FN:=0;
                          FOFFSET:=OFFSET + LINKSIZE(RLINKV);
                          (*all in-field links assumed 1 byte ! *)
                          (*move to field pointer now*)
(*$R-*)
                          WHILE (FN < FLINKNUM) DO
                            BEGIN
                              FOFFSET:=FOFFSET+WA^[FOFFSET];
                              FN:=FN+1;
                            END;
                          MOVELEFT(WA^[FOFFSET],S,WA^[FOFFSET]);
(*$R+*)
                          DELETE(S,LENGTH(S),1); (*correct link to length*)
                          CASE RULE OF
                            ASCENDING: DONE:= (KEY <= S);
                            DESCENDING:DONE:= (KEY >= S);
                            RANDOM:    DONE:= (KEY =  S)
                          END (*CASES*);
                          IF DONE THEN
                            BEGIN
                              FOUND:= (KEY = S);
                              GOTO 1; (*for efficiency*)
                            END
                          ELSE
                            BEGIN (*jump to next record*)
                              OFFSET:=OFFSET+RLINKV;
                              RLINKV:=LINKVALUE(WA,OFFSET);
                              RN:=RN+1;
                            END (*NOT DONE*);
                        END (*WHILE RLINKV*);
1:
                      RECNUM:=RN;
                      ITEMNUM:=RN;
                    END (*untagged ok*);
                END (*WITH RP^*);
          END (*LEVEL = RECORDT*);
      END (*WITH WIB^*);
  TRACEWA(28,WI);
END (*DBFINDREC*);


(*DATA TRANSFER PRIMITIVES*)
FUNCTION DBCOPY(*SOURCE,DESTINATION:DBWRKINDEX):DBERRTYPE*);
(*zero out the destination workarea. copy source record or group into
  destination. initialize pointers. *)
VAR SLEVEL:DBLEVELTYPE; 
  SINUSE,SDNUM,SOFFSET,SLINKV,STOS:INTEGER;
BEGIN
  TRACEWA(24,SOURCE);
  TRACEWA(25,DESTINATION);
  ZEROWORKAREA(DESTINATION);
  WITH WRKTABLE[SOURCE] DO
    WITH WIB^[TOS] DO
      BEGIN
        SINUSE:=SPACEINUSE;
        SLEVEL:=LEVEL;
        SOFFSET:=OFFSET;
        SLINKV:=LINKVALUE(WA,OFFSET);
        STOS:=TOS;
        SDNUM:=DESCRIPTORNUM;
      END;
  IF (SLEVEL <> GROUPT) OR (STOS <> 0) THEN
    DBCOPY:=12 (*can''t yet handle anything but outer level group*)
  ELSE
    WITH WRKTABLE[DESTINATION] DO
      WITH WIB^[TOS] DO
        IF SLINKV > WSIZE THEN
          DBCOPY:=1 (*insufficient space*)
        ELSE
          BEGIN
            SPACEINUSE:=SINUSE;
            LEVEL:=GROUPT;
            OFFSET:=0;
            DESCRIPTORNUM:=SDNUM;
            ITEMNUM:=0;
(*$R-*)
            MOVELEFT(WRKTABLE[SOURCE].WA^[SOFFSET],
                     WA^[OFFSET], SLINKV);
(*$R+*)
          END;
  TRACEWA(26,DESTINATION);
END (*DBCOPY*);

FUNCTION DBEMPTYITEM(*DESTINATION:DBWRKINDEX; LVL:DBLEVELTYPE;
                                              TAG:INTEGER):DBERRTYPE*);
(*creates a new empty item at level LVL and sets its tag if required*)
VAR NEWOFFSET,LINKV:PAGEPTR;
  TAGBYTES,ISTACK:INTEGER;

  PROCEDURE NEWLINKITEM(WIDTH:INTEGER; NEWOFFSET:PAGEPTR);
  (*insert an empty linked item WIDTH bytes wide*)
  VAR I:INTEGER;
  BEGIN
    IF TAG >= LINKESCAPE THEN WIDTH:=WIDTH+1;
    WITH WRKTABLE[DESTINATION] DO
      BEGIN
        DBSHOWERR('NEWLINKITEM', MOVETAIL(DESTINATION,WIDTH,NEWOFFSET));
(*$R-*)
        WA^[NEWOFFSET]:=WIDTH;
        IF LVL = GROUPT THEN
          SAVEBIGLINK(DESTINATION,TAG,NEWOFFSET+1);
(*$R+*)
        IF LVL = WIB^[TOS].LEVEL THEN
          BEGIN
            IF TOS > 0 THEN
              FIXLINKS(DESTINATION, (TOS-1), WIDTH);
          END
        ELSE
          FIXLINKS(DESTINATION, TOS, WIDTH);
      END (*WITH*);
  END (*NEWLINKITEM*);
  
  PROCEDURE BLANKRECORD;
  (*lay out empty fields in a blank record*)
  VAR RP:RECDESPTR;
    FP:FLDDESPTR;
    FN,MAXFN,FIXWIDTH,VARWIDTH:INTEGER;
    SW:CRACKSWTYPE;
    FIRSTLINKOFFSET:PAGEPTR;
  BEGIN
    WITH WRKTABLE[DESTINATION] DO
      WITH WIB^[TOS] DO
        BEGIN
          RP:=ACTIVERECORDS[DESCRIPTORNUM];
          WITH RP^ DO
            BEGIN
              FN:=0;
              SW.BL:=SWITCHES;
              FIRSTLINKOFFSET:=OFFSET+1;
              IF SW.A[0] THEN (*tagged*)
                FIXWIDTH:=1
              ELSE
                FIXWIDTH:=0;
              MAXFN:=LASTFLDLINK DIV 2 - 1;
              (*fixed fields first*)
              WHILE (FN < FIRSTLITEMNUM) AND (FN < MAXFN) DO
                BEGIN
(*$R-*)
                  WITH FLDREF[FN] DO
(*$R+*)
                    BEGIN
                      FP:=ACTIVEFIELDS[FDNUM];
                      FIXWIDTH:=FIXWIDTH+FP^.MAXWIDTH;
                    END;
                  FN:=FN+1;
                END (*WHILE*);
              IF FN > 0 THEN
                BEGIN
                  (*one link over all fixed fields*)
                  FIXWIDTH:=FIXWIDTH+1;
                  NEWLINKITEM(FIXWIDTH, FIRSTLINKOFFSET);
                END;
              (*if there are fixed fields, FIXWIDTH now includes the link*)
              NEWOFFSET:=FIRSTLINKOFFSET+FIXWIDTH;
              (*now put links of 1 for each variable size field*)
              VARWIDTH:=MAXFN-FN+1;
              DBEMPTYITEM:=MOVETAIL(DESTINATION, VARWIDTH, NEWOFFSET);
              WHILE FN < MAXFN DO
                BEGIN
(*$R-*)
                  WA^[NEWOFFSET]:=1;
(*$R+*)
                  NEWOFFSET:=NEWOFFSET+1;
                  FN:=FN+1;
                END;
            END (*WITH RP^*);
          (*still have to set overlink of record itself*)
          IF (VARWIDTH+FIXWIDTH) >= LINKESCAPE THEN
            BEGIN
              VARWIDTH:=VARWIDTH+1;
              DBEMPTYITEM:=MOVETAIL(DESTINATION,1,OFFSET);
            END;
          SAVEBIGLINK(DESTINATION, 
                (VARWIDTH+FIXWIDTH+1(*original link assumed small*)), 
                                          OFFSET);
          (* and also the enclosing links*)
          FIXLINKS(DESTINATION, (TOS-1), VARWIDTH);
        END (*WITH WIB^[TOS]*);
  END (*BLANKRECORD*);
  
BEGIN (*DBEMPTYITEM*)
  DBEMPTYITEM:=0;
  WITH WRKTABLE[DESTINATION] DO
    BEGIN
      TRACEWA(0,DESTINATION);
      WITH WIB^[TOS] DO
        IF LVL=LEVEL THEN
          CASE LEVEL OF
            NONET: DBEMPTYITEM:=13; (*undefined level*)
            RECORDT:
              (*insert a single byte link with value of 2, with nul stopper*)
              BEGIN
                NEWLINKITEM(1,OFFSET);
                IF DBTYPECHECK THEN
                  BLANKRECORD;
              END;
            GROUPT: 
              BEGIN
                NEWLINKITEM(3,OFFSET); (*leave byte for required tag*)
                DESCRIPTORNUM:=TAG;
              END;
            FIELDT:NEWLINKITEM(2,OFFSET)
          END (*CASE LEVEL*)
        ELSE
          BEGIN (*LVL<>LEVEL*)
            IF LVL<>NEXTLEVEL(LEVEL) THEN
              DBEMPTYITEM:=15 (*improper data level*)
            ELSE (*new embedded level, probably have to update earlier link*)
              BEGIN (*create blank linked item, descend to it, make blank
                                record if needed*)
                IF LVL = GROUPT THEN
                  TAGBYTES:=2
                ELSE
                  TAGBYTES:=0;
                NEWOFFSET:=OFFSET+1+ORD(LINKVALUE(WA,OFFSET) >= LINKESCAPE);
                IF LEVEL = GROUPT THEN (*step over the tag*)
                  NEWOFFSET:=NEWOFFSET+1
                               +ORD(LINKVALUE(WA,NEWOFFSET) >= LINKESCAPE);
                NEWLINKITEM(1+TAGBYTES,NEWOFFSET);
                DBEMPTYITEM:=DBDESCEND(DESTINATION);
                IF DBTYPECHECK AND (LVL = RECORDT) THEN
                  BLANKRECORD;
              END (*LVL = NEXTLEVEL*);
          END (*LVL<>LEVEL*);
    END (*WITH WRKTABLE*);
  TRACEWA(1,DESTINATION);
END (*DBEMPTYITEM*);

FUNCTION DBDELETE(*DESTINATION:DBWRKINDEX):DBERRTYPE*);
(*eliminate the destination item (group or record only) entirely*)
VAR LINKV:INTEGER;
BEGIN
  TRACEWA(17,DESTINATION);
  DBDELETE:=0;
  WITH WRKTABLE[DESTINATION] DO
    WITH WIB^[TOS] DO
      BEGIN
        IF NOT (LEVEL IN [GROUPT,RECORDT]) THEN
          DBDELETE:=41
        ELSE
          BEGIN
            LINKV:=LINKVALUE(WA,OFFSET);
            IF LINKV <> 0 THEN
              DBDELETE:=MOVETAIL(DESTINATION, -LINKV, OFFSET+LINKV);
            IF TOS > 0 THEN
              FIXLINKS(DESTINATION, TOS-1, -LINKV);
          END (*LEVEL OK*);
      END (*WITH WIB^*);
  TRACEWA(18,DESTINATION);
END (*DBDELETE*);

FUNCTION DBBLANK(*DESTINATION:DBWRKINDEX):DBERRTYPE*);
(*replace the destination group or record with an empty item*)
VAR RSLT,DELTA:INTEGER;
BEGIN
  TRACEWA(19,DESTINATION);
  RSLT:=DBDELETE(DESTINATION);
  IF RSLT <> 0 THEN
    DBBLANK:=RSLT
  ELSE
    WITH WRKTABLE[DESTINATION] DO
      WITH WIB^[TOS] DO
        BEGIN
          IF LEVEL = GROUPT THEN
            DELTA:=3
          ELSE
            DELTA:=2;
          RSLT:=MOVETAIL(DESTINATION, DELTA, OFFSET);
          IF RSLT <> 0 THEN
            DBBLANK:=RSLT
          ELSE
            BEGIN
              WA^[OFFSET]:=DELTA;
              IF TOS > 0 THEN
                FIXLINKS(DESTINATION, TOS-1, DELTA);
            END;
          DESCRIPTORNUM:=-1;
        END (*WITH WIB^*);
  TRACEWA(20,DESTINATION);
END (*DBBLANK*);

FUNCTION DBREPLACE(*SOURCE,DESTINATION:DBWRKINDEX):DBERRTYPE*);
(*the source item replaces the destination item. must be record or group*)
VAR SLINKV,SOFFSET,STOS,DOFFSET,DTOS,RSLT:INTEGER;
  SLEVEL,DLEVEL:DBLEVELTYPE;
BEGIN
  TRACEWA(21,SOURCE);
  TRACEWA(22,DESTINATION);
  RSLT:=DBDELETE(DESTINATION);
  IF RSLT <> 0 THEN
    DBREPLACE:=RSLT
  ELSE
    BEGIN
      WITH WRKTABLE[SOURCE] DO
        WITH WIB^[TOS] DO
          BEGIN
            SLEVEL:=LEVEL;
            SOFFSET:=OFFSET;
            SLINKV:=LINKVALUE(WA,OFFSET);
            STOS:=TOS;
          END;
      WITH WRKTABLE[DESTINATION] DO
        WITH WIB^[TOS] DO
          BEGIN
            DLEVEL:=LEVEL;
            DOFFSET:=OFFSET;
            DTOS:=TOS;
          END;
      IF DLEVEL <> SLEVEL THEN
        DBREPLACE:=42 (*mismatch*)
      ELSE
        IF NOT (DLEVEL IN [GROUPT,RECORDT]) THEN
          DBREPLACE:=41
        ELSE
          BEGIN
            (*open space up to receive source copy*)
            RSLT:=MOVETAIL(DESTINATION,SLINKV,DOFFSET);
            IF RSLT <> 0 THEN
              DBREPLACE:=RSLT
            ELSE
(*$R-*)
              BEGIN
                MOVELEFT(WRKTABLE[SOURCE].WA^[SOFFSET],
                       WRKTABLE[DESTINATION].WA^[DOFFSET], SLINKV);
(*$R+*)
                IF DTOS > 0 THEN
                  FIXLINKS(DESTINATION, DTOS-1, SLINKV);
              END;
            WRKTABLE[DESTINATION].WIB^[DTOS].DESCRIPTORNUM
              := WRKTABLE[SOURCE].WIB^[STOS].DESCRIPTORNUM;
          END (*levels ok*);
    END (*DELETE worked ok*);
  TRACEWA(23,DESTINATION);
END (*DBREPLACE*);

FUNCTION DBRESERVE(*DESTINATION:DBWRKINDEX):DBERRTYPE*);
(*reserve empty space at the end of destination group*)
BEGIN
  TRACEWA(32,DESTINATION);
  TRACEWA(33,DESTINATION);
END (*DBRESERVE*);

FUNCTION GETFOFFSET(WI:DBWRKINDEX):PAGEPTR;
(*returns the offset (in record) of a fixed width field based on
  its ITEMNUM*)
VAR RP:RECDESPTR;
 DN:INTEGER;
BEGIN
  WITH WRKTABLE[WI] DO
    BEGIN
      DN:=WIB^[TOS-1].DESCRIPTORNUM;
      IF (DN >= 0) AND (DN <= LASTRECDESCRIPTOR) THEN
        BEGIN
          RP:=ACTIVERECORDS[DN];
          IF RP = NIL THEN
            DBSHOWERR('GETOFFSET - Record not active', 100)
          ELSE
(*$R-*)
            GETFOFFSET:=RP^.FLDREF[WIB^[TOS].ITEMNUM].FLDOFFSET;
(*$R+*)
        END
      ELSE
        DBSHOWERR('GETOFFSET - DESCRIPTORNUM not initialized',100);
    END;
END (*GETFOFFSET*);

FUNCTION DBGET(*SOURCE:DBWRKINDEX):DBERRTYPE*);
(*extract item value from workarea and place it in DBMAIL for pickup by
  caller*)
CONST FIXEDWIDTH = 1;
VAR LINKV: INTEGER;
  FP:FLDDESPTR;
  RP:RECDESPTR;
  SW:CRACKSWTYPE;
  FOFFSET:INTEGER;

  PROCEDURE GETLINKF(FLDTYPE:DBFIELDTYPES);
  BEGIN
    WITH WRKTABLE[SOURCE] DO
      WITH WIB^[TOS] DO
        BEGIN
          LINKV:=LINKVALUE(WA,OFFSET);
          IF LINKV >= LINKESCAPE THEN
            DBGET:=21 (*string too long to assign*)
          ELSE
            BEGIN
(*$R-*)
              MOVELEFT(WA^[OFFSET],DBMAIL.TXT,LINKV);
(*$R+*)
              DBMAIL.TXT[0]:=CHR(LINKV-1);
              DBMAIL.DBMAILTYPE:=FLDTYPE;
            END (*LINKV < LINKESCAPE*);
        END (*WITH WIB*);
  END (*GETLINKF*);
  
BEGIN (*DBGET*)
  DBGET:=0;
  TRACEWA(13,SOURCE);
  IF DBTYPECHECK THEN
    WITH WRKTABLE[SOURCE] DO
      WITH WIB^[TOS] DO
        BEGIN
          IF LEVEL = GROUPT THEN
            GETLINKF(GROUPF)
          ELSE
            IF LEVEL <> FIELDT THEN
              DBGET:=38 (*must be a field*)
            ELSE
          IF (DESCRIPTORNUM >= 0) 
                  AND (DESCRIPTORNUM <= LASTFIELDDESCRIPTOR) THEN
            BEGIN
              FP:=ACTIVEFIELDS[DESCRIPTORNUM];
              IF FP = NIL THEN
                DBGET:=31 (*no such field exists*)
              ELSE
                WITH FP^ DO
                  BEGIN
                    SW.BL:=SWITCHES;
                    IF SW.A[FIXEDWIDTH] THEN
                      WITH DBMAIL DO
                        BEGIN
(*$R-*)
                          MOVELEFT(WA^[OFFSET
                                   +GETFOFFSET(SOURCE)], TXT, MAXWIDTH);
(*$R+*)
                          DBMAILTYPE:=FLDTYPE;
                        END
                    ELSE
                      GETLINKF(FLDTYPE);
                  END (*WITH FP^*);
            END (*DESCRIPTORNUM OK*)
          ELSE
            DBGET:=31; (*no such field exists*)
        END (*WITH WIB^[TOS]*)
  ELSE (*no type checking - assume it's linked*)
    GETLINKF(STRINGF);
END (*DBGET*);


